#include "linux/types.h"
#include "linux/irqchip.h"
#include "linux/slab.h"
#include "linux/bug.h"
#include "linux/ioport.h"
#include "linux/of.h"
#include "linux/errno.h"
#include "linux/printk.h"
#include "linux/of_irq.h"
#include "linux/irq.h"
#include "asm/mach/arch.h"
#include "linux/of_platform.h"

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
    struct of_phandle_args oirq;

    if (of_irq_parse_one(dev, index, &oirq))
        return 0;

    return irq_create_of_mapping(&oirq);
}

struct device_node *of_irq_find_parent(struct device_node *child)
{
    struct device_node *p;
     __be32 *parp;

    if (!of_node_get(child))
        return NULL;

    do
    {
        parp = of_get_property(child, "interrupt-parent", NULL);
        if (parp == NULL)
            p = of_get_parent(child);
        else
        {
            // if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
            // p = of_node_get(of_irq_dflt_pic);
            // else
            p = of_find_node_by_phandle(be32_to_cpup(parp));
        }
        of_node_put(child);
        child = p;
    } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);

    return p;
}

/*
 * 功能：这是一个底层的中断树解析函数，用于解析设备树中中断的描述信息，支持复杂的 interrupt-map 映射关系。
 * 参数：
 * addr：设备的地址描述符（通常是 "reg" 属性的起始部分），以大端 32 位格式传入。用于匹配 interrupt-map。
 * out_irq：输入/输出参数，类型为 struct of_phandle_args *，用于保存解析出来的中断控制器节点和中断参数。
 */
int of_irq_parse_raw( __be32 *addr, struct of_phandle_args *out_irq)
{
    //int i;
    //__be32 initial_match_array[MAX_PHANDLE_ARGS];
    struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
     __be32 *tmp;
    uint32_t intsize = 1, addrsize;

    ipar = of_node_get(out_irq->np);

    /*
        设备树中断描述符的长度由 #interrupt-cells 决定，如果当前节点没有该属性，需要向上查找父节点，直到找到为止。
        如果找不到，说明中断树结构有问题，直接失败
     */
    do
    {
        tmp = of_get_property(ipar, "#interrupt-cells", NULL);
        if (tmp != NULL)
        {
            intsize = be32_to_cpu(*tmp);
            break;
        }
        tnode = ipar;
        ipar = of_irq_find_parent(ipar);
        of_node_put(tnode);
    } while (ipar);
    if (ipar == NULL)
    {
        printk(" -> no parent found !\n");
        goto fail;
    }

    printk("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);

    /*
        检查传入的参数数量是否和 #interrupt-cells 匹配,  参数数量不匹配说明设备树描述有误，无法继续解析
    */
    if (out_irq->args_count != intsize)
        return -EINVAL;

    /*
        查找当前节点或其父节点的 #address-cells 属性，确定地址参数的单元数。
        有些设备树节点没有 #address-cells，需要向上查找。默认值为 2，兼容旧的设备树写法
     */
    old = of_node_get(ipar);
    do
    {
        tmp = of_get_property(old, "#address-cells", NULL);
        tnode = of_get_parent(old);
        of_node_put(old);
        old = tnode;
    } while (old && tmp == NULL);
    of_node_put(old);
    old = NULL;
    addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);

    printk(" -> addrsize=%d\n", addrsize);

    /* 防止参数数组溢出 */
    if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS))
        goto fail;

    /* 将地址参数和中断参数拼接到 match_array 中 */
    //for (i = 0; i < addrsize; i++)
        //initial_match_array[i] = addr ? addr[i] : 0;
    //for (i = 0; i < intsize; i++)
        //initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);

    /* 循环遍历中断树，查找最终的中断控制器,设备树可能有多级中断映射，需要逐级解析 */
    while (ipar != NULL)
    {
        /* 中断控制器节点是解析的终点 */
        if (of_get_property(ipar, "interrupt-controller", NULL) !=
            NULL)
        {
            printk(" -> got it !\n");
            return 0;
        }

        /* 如果不是中断控制器，则要处理中断映射相关的东西，很多情况下都是用不到的，需要的时候再做添加 */
        
        //...

    }
fail:
    of_node_put(ipar);
    of_node_put(newpar);

    return -EINVAL;
}

int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{
    struct device_node *p;
     __be32 *intspec, *tmp, *addr;
    uint32_t intsize, intlen;
    int i, res;

    printk("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);

    /* 获取 reg 属性 */
    addr = of_get_property(device, "reg", NULL);

    /* 优先尝试解析新风格的interrupts-extended 属性 */
    res = of_parse_phandle_with_args(device, "interrupts-extended", "#interrupt-cells", index, out_irq);

    /* 如果有则调用of_irq_parse_raw进一步处理并返回结果 */
    if (!res)
        return of_irq_parse_raw(addr, out_irq);

    /* 获取interrupts属性,没有则返回出错 */
    intspec = of_get_property(device, "interrupts", &intlen);
    if (intspec == NULL)
        return -EINVAL;

    /* 
        intlen获取到的是 interrupts 属性的总长度，现在转换为占多少个32位单元数。
        例如，interrupts = <1 2 3 4 5 6>，总长度为24，转换为占多少个32位数则为 24 / 4 = 6 
    */
    intlen /= sizeof(*intspec);
 
    printk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);

    /* 查找父节点 */
    p = of_irq_find_parent(device);
    if (p == NULL)
        return -EINVAL;

    tmp = of_get_property(p, "#interrupt-cells", NULL);
    if (tmp == NULL)
    {
        res = -EINVAL;
        goto out;
    }
    intsize = be32_to_cpu(*tmp);
    /* 
        获取父节点的 "#interrupt-cells" 属性，表示每个中断描述符的单元数。如果没有该属性，返回错误 
        假设：
        #interrupt-cells = <3>，interrupts = <10 20 30 40 50 60>，
        则有2组中断描述符，每组3个单元：

        第0组：10 20 30
        第1组：40 50 60
        这里 intsize = 3，intlen = 6。
    */

    printk(" intsize=%d intlen=%d\n", intsize, intlen);
 
    /* 检查 index 是否越界。如果 index 超出可用中断数量，返回错误 */
    if ((index + 1) * intsize > intlen)
    {
        res = -EINVAL;
        goto out;
    }

    /* 定位到指定 index 的中断描述符 */
    intspec += index * intsize;
    out_irq->np = p; /* 设置父节点 */
    out_irq->args_count = intsize; /* 设置参数数量 */
    for (i = 0; i < intsize; i++)
        out_irq->args[i] = be32_to_cpup(intspec++); /* 复制中断描述符的每个单元 */

    /* 调用 of_irq_parse_raw 检查是否需要中断映射转换（如有 interrupt-map 属性）*/
    res = of_irq_parse_raw(addr, out_irq);
out:
    of_node_put(p);
    return res;
}

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
{
    int irq = irq_of_parse_and_map(dev, index);

    if (r && irq)
    {
         char *name = NULL;

        memset(r, 0, sizeof(*r));
        /*
         * Get optional "interrupt-names" property to add a name
         * to the resource.
         */
        of_property_read_string_index(dev, "interrupt-names", index,
                                      &name);

        r->start = r->end = irq;
        r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
        r->name = name ? name : of_node_full_name(dev);
    }

    return irq;
}

int of_irq_get(struct device_node *dev, int index)
{
    int rc;
    struct of_phandle_args oirq;
    struct irq_domain *domain;

    rc = of_irq_parse_one(dev, index, &oirq);
    if (rc)
        return rc;

    domain = irq_find_host(oirq.np);
    if (!domain)
        return -EINVAL;

    return irq_create_of_mapping(&oirq);
}

int of_irq_get_byname(struct device_node *dev,  char *name)
{
    int index;

    if (!name)
        return -EINVAL;

    index = of_property_match_string(dev, "interrupt-names", name);
    if (index < 0)
        return index;

    return of_irq_get(dev, index);
}

int of_irq_count(struct device_node *dev)
{
    struct of_phandle_args irq;
    int nr = 0;

    while (of_irq_parse_one(dev, nr, &irq) == 0)
        nr++;

    return nr;
}

int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs)
{
    int i;

    for (i = 0; i < nr_irqs; i++, res++)
        if (!of_irq_to_resource(dev, i, res))
            break;

    return i;
}

struct intc_desc
{
    struct list_head list;
    struct device_node *dev;
    struct device_node *interrupt_parent;
};

void __init of_irq_init(struct of_device_id *matches)
{
    struct device_node *np, *parent = NULL;
    struct intc_desc *desc, *temp_desc;
    struct list_head intc_desc_list, intc_parent_list;

    INIT_LIST_HEAD(&intc_desc_list);
    INIT_LIST_HEAD(&intc_parent_list);

    for_each_matching_node(np, matches)
    {
        if (!of_find_property(np, "interrupt-controller", NULL) || !of_device_is_available(np))
            continue;
        /*
         * Here, we allocate and populate an intc_desc with the node
         * pointer, interrupt-parent device_node etc.
         */
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);
        if (WARN_ON(!desc))
            goto err;

        desc->dev = np;
        desc->interrupt_parent = of_irq_find_parent(np);
        if (desc->interrupt_parent == np)
            desc->interrupt_parent = NULL;
        list_add_tail(&desc->list, &intc_desc_list);
    }

    /*
     * The root irq controller is the one without an interrupt-parent.
     * That one goes first, followed by the controllers that reference it,
     * followed by the ones that reference the 2nd level controllers, etc.
     */
    while (!list_empty(&intc_desc_list))
    {
        /*
         * Process all controllers with the current 'parent'.
         * First pass will be looking for NULL as the parent.
         * The assumption is that NULL parent means a root controller.
         */
        list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list)
        {
           struct of_device_id *match;
            int ret;
            of_irq_init_cb_t irq_init_cb;

            if (desc->interrupt_parent != parent)
                continue;

            list_del(&desc->list);
            match = of_match_node(matches, desc->dev);
            if (WARN(!match->data, "of_irq_init: no init function for %s\n", match->compatible))
            {
                kfree(desc);
                continue;
            }

            printk("of_irq_init: init %s @ %p, parent %p\n", match->compatible, desc->dev, desc->interrupt_parent);
            irq_init_cb = (of_irq_init_cb_t)match->data;
            ret = irq_init_cb(desc->dev, desc->interrupt_parent);
            if (ret)
            {
                kfree(desc);
                continue;
            }

            /*
             * This one is now set up; add it to the parent list so
             * its children can get processed in a subsequent pass.
             */
            list_add_tail(&desc->list, &intc_parent_list);
        }

        /* Get the next pending parent that might have children */
        desc = list_first_entry_or_null(&intc_parent_list, typeof(*desc), list);
        if (!desc)
        {
            printk("of_irq_init: children remain, but no parents\n");
            break;
        }
        list_del(&desc->list);
        parent = desc->dev;
        kfree(desc);
    }

    list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list)
    {
        list_del(&desc->list);
        kfree(desc);
    }
err:
    list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list)
    {
        list_del(&desc->list);
        kfree(desc);
    }
}


void __init init_IRQ(void)
{

    //if (!machine_desc->init_irq)
        irqchip_init();
    //else
        //machine_desc->init_irq();
}